﻿using System.Diagnostics.CodeAnalysis;
using Bzstd.Utility.Assertion;

namespace Bzstd.Utility.Date;

/// <summary>
/// 节日
/// </summary>
[SuppressMessage("ReSharper", "IdentifierTypo")]
public readonly struct Festival
{
	private DateTime Date { get; }

	private string Name { get; }

	/// <summary>
	/// 公历节日
	/// </summary>
	private static readonly Festival[] finalFestival =
	[
		/* 元旦 (1月1日) */
		new(DateTime.Parse("1900-1-1"), "元旦"),
		/* 情人节 (2月14日) */
		new(DateTime.Parse("1900-2-14"), "情人节"),
		/* 妇女节 (3月8日) */
		new(DateTime.Parse("1900-3-8"), "妇女节"),
		/* 植树节 (3月12日) */
		new(DateTime.Parse("1900-3-12"), "植树节"),
		/* 愚人节 (4月1日) */
		new(DateTime.Parse("1900-4-1"), "愚人节"),
		/* 劳动节 (5月1日) */
		new(DateTime.Parse("1900-5-1"), "劳动节"),
		/* 青年节 (5月4日) */
		new(DateTime.Parse("1900-5-4"), "青年节"),
		/* 儿童节 (6月1日) */
		new(DateTime.Parse("1900-6-1"), "儿童节"),
		/* 建党节 (7月1日) */
		new(DateTime.Parse("1900-7-1"), "建党节"),
		/* 建军节 (8月1日) */
		new(DateTime.Parse("1900-8-1"), "建军节"),
		/* 教师节 (9月10日) */
		new(DateTime.Parse("1900-9-10"), "教师节"),
		/* 国庆节 (10月1日) */
		new(DateTime.Parse("1900-10-1"), "国庆节"),
		/* 程序员节 (10月24日) */
		new(DateTime.Parse("1900-10-24"), "程序员节"),
		/* 万圣节 (11月1日) */
		new(DateTime.Parse("1900-11-1"), "万圣节"),
		/* 万灵节 (11月2日) */
		new(DateTime.Parse("1900-11-2"), "万灵节"),
		/* 光棍节 (11月11日) */
		new(DateTime.Parse("1900-11-11"), "光棍节"),
		/* 平安夜 (12月24日) */
		new(DateTime.Parse("1900-12-24"), "平安夜"),
		/* 圣诞节 (12月25日) */
		new(DateTime.Parse("1900-12-25"), "圣诞节")
	];

	/// <summary>
	/// 农历节日
	/// </summary>
	private static readonly Festival[] lunarFestival =
	[
		/* 春节 (正月初一) */
		new(DateTime.Parse("1900-1-1"), "春节"),
		/* 元宵节(正月十五) */
		new(DateTime.Parse("1900-1-15"), "元宵节"),
		/* 龙抬头(二月初二) */
		new(DateTime.Parse("1900-2-2"), "龙抬头"),
		/* 端午节(五月初五) */
		new(DateTime.Parse("1900-5-5"), "端午节"),
		/* 七夕(七月初七) */
		new(DateTime.Parse("1900-7-7"), "七夕"),
		/* 中元节(七月十五) */
		new(DateTime.Parse("1900-7-15"), "中元节"),
		/* 中秋节(八月十五) */
		new(DateTime.Parse("1900-8-15"), "中秋节"),
		/* 重阳节(九月初九) */
		new(DateTime.Parse("1900-9-9"), "重阳节"),
		/* 腊八(腊月初八) */
		new(DateTime.Parse("1900-12-8"), "腊八"),
		/* 小年(腊月廿三) */
		new(DateTime.Parse("1900-12-23"), "小年")
	];

	private Festival(DateTime date, string name)
	{
		Name = name;
		Date = date;
	}

	/// <summary>
	/// 母亲节，5月第2个星期六
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	private static bool IsMotherDay(DateTime date)
	{
		if (date.Month != 5)
		{
			return false;
		}

		if (date.DayOfWeek != DayOfWeek.Saturday)
		{
			return false;
		}

		if (GetWeekOfMonth(date) != 2)
		{
			return false;
		}

		return true;
	}

	/// <summary>
	/// 父亲节，6月第3个星期日
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	private static bool IsFatherDay(DateTime date)
	{
		if (date.Month != 6)
		{
			return false;
		}

		if (date.DayOfWeek != DayOfWeek.Sunday)
		{
			return false;
		}

		if (GetWeekOfMonth(date) != 3)
		{
			return false;
		}

		return true;
	}

	/// <summary>
	/// 感恩节，11月第4个星期四
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	private static bool IsThanksDay(DateTime date)
	{
		if (date.Month != 11)
		{
			return false;
		}

		if (date.DayOfWeek != DayOfWeek.Thursday)
		{
			return false;
		}

		if (GetWeekOfMonth(date) != 4)
		{
			return false;
		}

		return true;
	}

	/// <summary>
	/// 除夕，大月腊月三十，小月腊月廿九
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	private static bool IsChuxi(DateTime date)
	{
		var lunarDate = new Lunar(date);
		if (lunarDate.Month != 12)
		{
			return false;
		}

		if (lunarDate.IsLeap)
		{
			return false;
		}

		if (lunarDate.Day < 29)
		{
			return false;
		}

		if (lunarDate.IsBigMonth() && lunarDate.Day == 29)
		{
			return false;
		}

		return true;
	}

	private static int GetWeekOfMonth(DateTime date)
	{
		return date.Day / 7 + (0 == date.Day % 7 ? 0 : 1);
	}

	/// <summary>
	/// 获取一个日期的节日
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	public static string[] GetFestival(DateTime date)
	{
		Assert.IsTrue(date > DateTime.MinValue, "date不能为空");
		var lunar = new Lunar(date);
		return new[]
		{
			finalFestival.Where(f => f.Date.Month == date.Month && f.Date.Day == date.Day).Select(f => f.Name),
			lunarFestival.Where(f => f.Date.Month == lunar.Month && f.Date.Day == lunar.Day).Select(f => f.Name),
			new[] {"母亲节"}.Where(s => IsMotherDay(date)),
			new[] {"父亲节"}.Where(s => IsFatherDay(date)),
			new[] {"感恩节"}.Where(s => IsThanksDay(date)),
			new[] {"除夕"}.Where(s => IsChuxi(date))
		}.SelectMany(s => s).ToArray();
	}
}
